package utils

import (
	"encoding/binary"
	"fmt"
	"google.golang.org/protobuf/proto"
	"io"
	"net"
	"time"

	"go.uber.org/zap"

	pb "go-war3proxy/pkg/proto"
)

// TransferData 双向转发数据
func TransferData(src net.Conn, dst net.Conn, logger *zap.Logger) {
	logger.Info("开始数据转发", zap.String("src", src.RemoteAddr().String()), zap.String("dst", dst.RemoteAddr().String()))

	defer func() {
		src.Close()
		dst.Close()
	}()

	buffer := make([]byte, 1024)
	for {
		// 设置读取超时
		src.SetReadDeadline(time.Now().Add(30 * time.Second))

		n, err := src.Read(buffer)
		if err != nil {
			if err != io.EOF && !isTimeoutError(err) {
				logger.Error("读取数据失败", zap.Error(err))
			}
			break
		}

		if n > 0 {
			_, err = dst.Write(buffer[:n])
			if err != nil {
				logger.Error("写入数据失败", zap.Error(err))
				break
			}
		}
	}
}

// isTimeoutError 检查是否为超时错误
func isTimeoutError(err error) bool {
	if netErr, ok := err.(net.Error); ok {
		return netErr.Timeout()
	}
	return false
}

// SendProtobufMessage 发送protobuf消息
func SendProtobufMessage(conn net.Conn, message *pb.War3Message, logger *zap.Logger) error {
	logger.Info("发送protobuf消息",
		zap.String("type", message.Type.String()),
		zap.String("msg", message.String()))

	messageBytes, err := proto.Marshal(message)
	if err != nil {
		return fmt.Errorf("序列化消息失败: %w", err)
	}

	// 先发送消息长度(4字节)
	lengthBytes := make([]byte, 4)
	binary.BigEndian.PutUint32(lengthBytes, uint32(len(messageBytes)))

	// 发送长度
	if _, err := conn.Write(lengthBytes); err != nil {
		return fmt.Errorf("发送消息长度失败: %w", err)
	}

	// 发送消息内容
	if _, err := conn.Write(messageBytes); err != nil {
		return fmt.Errorf("发送消息内容失败: %w", err)
	}

	return nil
}

// ReceiveProtobufMessage 接收protobuf消息
func ReceiveProtobufMessage(conn net.Conn, logger *zap.Logger) (*pb.War3Message, error) {
	// 读取消息长度
	lengthBytes := make([]byte, 4)
	if _, err := io.ReadFull(conn, lengthBytes); err != nil {
		return nil, fmt.Errorf("读取消息长度失败: %w", err)
	}

	messageLength := binary.BigEndian.Uint32(lengthBytes)
	if messageLength > 1024*1024 { // 限制最大消息大小为1MB
		return nil, fmt.Errorf("消息长度过大: %d", messageLength)
	}

	// 读取消息内容
	messageBytes := make([]byte, messageLength)
	if _, err := io.ReadFull(conn, messageBytes); err != nil {
		return nil, fmt.Errorf("读取消息内容失败: %w", err)
	}

	// 解析消息
	message := &pb.War3Message{}
	if err := proto.Unmarshal(messageBytes, message); err != nil {
		return nil, fmt.Errorf("解析消息失败: %w", err)
	}

	logger.Info("接收protobuf消息",
		zap.String("type", message.Type.String()),
		zap.String("msg", message.String()))

	return message, nil
}

// CloseConn 安全关闭连接
func CloseConn(conn net.Conn) {
	if conn != nil {
		conn.Close()
	}
}
